package com.hbx.service.impl;

import com.hbx.constant.DateFormatContent;
import com.hbx.constant.ResponseStateConstant;
import com.hbx.constant.SysLogEnum;
import com.hbx.constant.TableEnum;
import com.hbx.logs.annotation.MarkLog;
import com.hbx.logs.OperaTypeEnum;
import com.hbx.dto.LogSearchParamDto;
import com.hbx.dto.SysLogDto;
import com.hbx.logs.es.EsTemplate;
import com.hbx.logs.SearchResp;
import com.hbx.exception.RspException;
import com.hbx.service.LogsService;
import com.hbx.utils.DateUtil;
import com.hbx.utils.Result;
import com.hbx.vo.logs.LogsPageVo;
import com.hbx.vo.logs.LogsVo;
import com.hbx.vo.logs.SysLogParamVo;
import com.hbx.vo.logs.SysLogVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/*
 * @Author:hbx
 * 
 * */
@Service
@Slf4j
public class LogsServiceImpl implements LogsService {

    @Autowired
    private EsTemplate esTemplate;

    @Value("${logging.path}")
    private String path;

    /**
     * 查询操作日志
     * @param logDto
     * @return
     */
    @Override
    public Result<LogsPageVo> getLogsInfo(LogSearchParamDto logDto) {
        if (ObjectUtils.isEmpty(logDto.getPageNum()) || logDto.getPageNum() < 1){
            throw new RspException(ResponseStateConstant.PARAMETER_ERROR,"pageNum为必传项且必须大于0");
        }
        if (ObjectUtils.isEmpty(logDto.getPageSize()) || logDto.getPageSize() < 1){
            throw new RspException(ResponseStateConstant.PARAMETER_ERROR,"pageSize为必传项且必须大于0");
        }
        Result<LogsPageVo> r = new Result<>();
        LogsPageVo vo = new LogsPageVo();
        SearchResp logSearchResult =  esTemplate.getLogsCondition(logDto);
        if (!ObjectUtils.isEmpty(logSearchResult)){
            vo.setTotal(logSearchResult.getTotal());
            vo.setPageNum(logSearchResult.getPageNum());
            vo.setPageSize(logSearchResult.getPageSize());
            List<LogsVo> list = new ArrayList<>();
            logSearchResult.getLogsList().stream().forEach(o -> {
                LogsVo logsVo = new LogsVo();
                BeanUtils.copyProperties(o,logsVo);
                list.add(logsVo);
            });
            vo.setLogsList(list);
            r.setData(vo).setCode(200);
        } else {
            r.setCode(201).setMsg("数据查询失败");
        }
        return r;
    }

    /**
     * 获取系统日志
     * @param logDto
     * @param r
     */
    @Override
    @MarkLog(operate = OperaTypeEnum.SELECT, tables = TableEnum.DEFAULT, desc = "管理员查看系统日志")
    public void getSysLogInfo(SysLogDto logDto, Result r) {
        Date date = logDto.getDate();
        Integer type = logDto.getType();
        if (type == null){
            type = 2;
        }
        String logType = SysLogEnum.getType(type);
        if (logType == null){
            r.setCode(201).setMsg("获取失败");
            return;
        }
        String dateFormat = DateUtil.dateToString(date, DateFormatContent.YYYY_MM_DD);
        if (dateFormat == null){
            r.setCode(201).setMsg("获取失败");
            return;
        }

        String logPath = path + File.separator + dateFormat + File.separator + "res." + logType + ".log";
        File file = new File(logPath);
        if (!file.exists()){
            r.setCode(201).setMsg("获取失败,文件不存在");
            return;
        }

        FileInputStream is = null;
        InputStreamReader reader = null;
        BufferedReader bufferedReader = null;
        List<String> list = new ArrayList<>();
        try {
            is = new FileInputStream(file);
            reader = new InputStreamReader(is,"UTF-8");
            bufferedReader = new BufferedReader(reader);
            String line = null;
            StringBuilder sb = new StringBuilder();
            while ((line=bufferedReader.readLine())!=null) {
                sb.append("<div>");
                sb.append(line);
                sb.append("</div>");
            }
            SysLogVo vo = new SysLogVo();
            vo.setLogContent(list);
            r.setCode(200).setData(sb);
            return;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (bufferedReader != null){
                    bufferedReader.close();
                }
                if (reader != null){
                    reader.close();
                }
                if (is != null){
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        r.setCode(201).setMsg("获取失败");
    }

    /**
     * 获得系统日志的参数配置
     * @param r
     */
    @Override
    public void getSysInitParam(Result r) {
        LocalDate now = LocalDate.now();
        int monthValue = now.getMonthValue();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
        SysLogParamVo vo = new SysLogParamVo();
        SysLogParamVo.DateInfo dateInfo = new SysLogParamVo.DateInfo();
        List<String> months = new ArrayList<>();
        for (int i = 1; i <= monthValue ; i++) {
            LocalDate month = now.withMonth(i);
            months.add(month.format(formatter));
        }
        List<SysLogParamVo.SysLogLevel> levels = new ArrayList<>();
        Map<String, List<String>> days = new HashMap<>();
        File file = new File(path);
        if(file.exists()){
            String[] fileNames = file.list();
            if (!ObjectUtils.isEmpty(fileNames)){
                for (String month : months) {
                    List<String> daysList = new ArrayList<>();
                    for (String fileItem : fileNames) {
                        if (fileItem.contains(month)) {
                            daysList.add(fileItem);
                        }
                    }
                    days.put(month, daysList);
                }
            }
            dateInfo.setMonths(months);
            dateInfo.setDays(days);
            vo.setDateInfos(dateInfo);
        }
        for (SysLogEnum logEnum : SysLogEnum.values()) {
            SysLogParamVo.SysLogLevel level = new SysLogParamVo.SysLogLevel();
            level.setCode(logEnum.getId());
            level.setType(logEnum.getType());
            levels.add(level);
        }
        vo.setLevels(levels);
        r.setCode(200).setData(vo);
    }

    /**
     * 管理员下载日志
     * @param logDto
     * @param resp
     */
    @Override
    @MarkLog(operate = OperaTypeEnum.SELECT, tables = TableEnum.DEFAULT, desc = "管理员下载系统日志")
    public void downLoadlog(SysLogDto logDto, HttpServletResponse resp) {
        Date date = logDto.getDate();
        String dateStr = DateUtil.dateToString(date, DateFormatContent.YYYY_MM_DD);
        if (!StringUtils.hasLength(dateStr)){
            throw new RspException("日志下载失败,date传递错误");
        }
        String type = SysLogEnum.getType(logDto.getType());
        if (!StringUtils.hasLength(type)){
            throw new RspException("日志下载失败,type传递错误");
        }
        String basePath = path;
        String fileName = dateStr + File.separator + "res." + type + ".log";

        FileInputStream is = null;
        try {
            //获取文件输入流
            is = new FileInputStream(new File(path, fileName));
            //附件下载
            resp.setHeader("content-disposition","attachment;fileName=" + URLEncoder.encode(fileName, "UTF-8"));
            //获取响应输出流
            ServletOutputStream os = resp.getOutputStream();
            //文件拷贝
            IOUtils.copy(is, os);
            IOUtils.closeQuietly(is);
            IOUtils.closeQuietly(os);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RspException("文件下载失败");
        } finally {
            try {
                if (is != null){
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 管理员清理系统日志
     * @param cleanTime
     * @param r
     * @return
     */
    @Override
    public void clearSysLog(Integer cleanTime, Result r) {
        List<String> dateRange = getDateRange(cleanTime);
        if (CollectionUtils.isEmpty(dateRange)) {
            r.setCode(400).setMsg("入参错误");
            return;
        }
        File baseFile = new File(path);
        if (!baseFile.exists()){
            r.setCode(201).setMsg("操作错误，请联系管理员");
            log.error("{}文件夹不存在", path);
            return;
        }
        File[] listFiles = baseFile.listFiles();
        if (listFiles != null && listFiles.length > 0){
            for (File listFile : listFiles) {
                dateRange.stream().forEach(date -> {
                    if (listFile.getName().contains(date)) {
                        boolean delete = FileSystemUtils.deleteRecursively(listFile);
                        if (!delete) {
                            log.error("文件{}删除失败", listFile.getName());
                        }
                    }
                });
            }
        }
        r.setCode(200).setMsg("清理成功");
    }

    @Override
    public void clearOperaLog(Integer cleanTime, Result r) {
        List<String> dateTimeRange = getDateTimeRange(cleanTime);
        if (CollectionUtils.isEmpty(dateTimeRange)) {
            r.setCode(400).setMsg("入参错误");
            return;
        }
        try {
            esTemplate.clearLog(dateTimeRange);
            r.setCode(200).setMsg("清理成功");
        } catch (Exception e) {
            e.printStackTrace();
            r.setCode(201).setMsg("清理失败");
        }
    }

    /**
     * 获得时间清理范围
     * @param monthNum
     * @return
     */
    private List<String> getDateRange(Integer monthNum){
        if (monthNum < 0){
            return new ArrayList<>(0);
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM");
        LocalDate now = LocalDate.now();
        List<String> result = new ArrayList<>();
        for (int i = monthNum; i > 0; i--) {
            LocalDate startTime = now.minusMonths(Long.parseLong(i + ""));
            result.add(startTime.format(formatter));
        }
        return result;
    }

    /**
     * 获得时间清理范围
     * @param monthNum
     * @return
     */
    private List<String> getDateTimeRange(Integer monthNum){
        if (monthNum < 0){
            return new ArrayList<>(0);
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime now = LocalDateTime.now();
        List<String> result = new ArrayList<>();
        LocalDateTime start = now.minusMonths(monthNum).withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0);
        LocalDateTime end = now.minusMonths(1).with(TemporalAdjusters.lastDayOfMonth()).withHour(23).withMinute(59).withSecond(59);
        result.add(start.format(formatter));
        result.add(end.format(formatter));
        return result;
    }
}
